/** @file    ButlerBot.cpp
 *  @author  Alessandro Polo
 *  @version $Id: ButlerBot.cpp 2581 2010-05-27 15:08:17Z alex $
 *  @brief
 * File containing methods for the ButlerBot class.
 * The header for this class can be found in ButlerBot.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "ButlerBot.h"

 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/Fact.h>
 #include <core/MethodsCommon.h>
 #include <core/Table.h>
 #include <core/Bundle.h>
 #include <core/NetworkManager.h>
 #include <core/SecurityManager.h>
 #include <core/BusManager.h>
 #include <core/Utilities.h>

 #include <interfaces/services/Butler.h>
 #include <interfaces/services/CommunicatorService.h>
 #include <framework/communication/TextMessage.h>

 #include <framework/building/Light.h>

using namespace std;
using namespace wosh;
using namespace wosh::communication;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

ButlerBot::ButlerBot( BundleGeneric& bundle ) : BundleGenericWorker(bundle)
 {
	this->currentLanguage = "en_GB";
 }

ButlerBot::~ButlerBot() {
	if ( isThreadRunning() ) {
		quitThread(10000);
	 }

	this->notifications.free();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

void ButlerBot::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	bool cProcessed = false;
	while( this->running )
	 {
		ThreadImpl::sleepForMSec(1000);
		setThreadAlive();

		// walk the notification queue
		this->notifications.mutex().lockForWrite();
		std::vector<Notification*>::iterator it;
		std::vector<Notification*>::iterator it_end = this->notifications.stdEnd();
		for ( it=this->notifications.stdBegin(); it!=it_end; it++ ) {
			if ( *it == NULL ) {
				this->notifications.erase_(it);
				break;
			 }
			cProcessed = processNotification_( *it );
			if ( !cProcessed ) break; // reset the iterator
		 }
		this->notifications.mutex().unLock();
	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool ButlerBot::processNotification_( Notification* notif )
 {
	if ( notif == NULL ) return true;
	if ( notif->isSent() ) return true;

	Message* reply = new Message( new Response( _CommunicatorService_RESPONSE_notif_read, new Variant(notif->getID()), WRET_OK ) );
	reply->setSource( this->getOwner() );
	reply->getDestination().fromString( "*:Communication" );
	reply->setDestinationBus(_Bus_Core);
	reply->setSecurityToken( SecurityManager::generateToken(this->getOwner()->getPermission().getOwner()) );
	BusCore.postMessage(reply);

	Log(LOG_INFO, ":processNotification_(): Parsing Notification#%ld", notif->getID() );

	WRESULT replied = WRET_ERR_INTERNAL;
	string fInput = notif->getPlain();
	Utilities::toLower(fInput);

	if ( fInput == "hi" || fInput == "hey" || fInput == "hello" ) {
		replied = replyTo_( notif, "Hi My Lord" );
	 }

	else if ( Utilities::compare_string_X(fInput, "lights off") > 80 ) {
		replied = sendRequest( notif, new Request(_Appliance_METHOD_off), URI(":Building/*[ObjectType="_Light_TYPE"]") );
	 }

	else
		replied = replyTo_( notif, "Sorry i don't understand" );

	if ( WSUCCEEDED(replied) ) {
		notif->setSent(true);
	 }
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ButlerBot::replyTo_( const Notification* source_notif, const string& message )
 {
	if ( source_notif == NULL ) return WRET_ERR_PARAM;

	TextMessage* txtmsg = new TextMessage();
	txtmsg->setSender_User( _Butler_USER );
	txtmsg->setRecipent_User( source_notif->getSender_User() );
	txtmsg->setProtocol( Notification::PROTOCOL_HUMAN );
	txtmsg->setLanguage( this->currentLanguage );
	txtmsg->setPlain( message );

	Message* msg_event = new Message();
	msg_event->setSource( this->getOwner() );
	msg_event->setContent( txtmsg );
	msg_event->setDestinationBroadcast(true);
	msg_event->setDestinationBus(_Bus_Core);
	msg_event->setSecurityToken( SecurityManager::generateToken(_Butler_USER) );
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ButlerBot::evaluateNotification( const Notification* notif )
 {
	if ( notif == NULL ) return WRET_ERR_PARAM;
	if ( notif->getLanguage() != this->currentLanguage ) {
		Log(LOG_WARNING, ":evaluateNotification(%ld): INVALID Language [%s!=%s]", notif->getID(), notif->getLanguage().c_str(), this->currentLanguage.c_str() );
		return WRET_ERR_PARAM;
	 }
	Log(LOG_VERBOSE, ":evaluateNotification(): Appended Notification#%ld", notif->getID() );
	Notification* localNotif = notif->clone();
	localNotif->setSent(false);
	this->notifications.add( localNotif );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ButlerBot::sendRequest( const Notification* source_notif, MessageContent* content, const URI& uri )
 {
	if ( source_notif == NULL ) return WRET_ERR_PARAM;

	Message* msg_event = new Message( content );
	msg_event->setSource( this->getOwner() );
	msg_event->setDestination(uri);
	msg_event->setDestinationBus(_Bus_Building);
	msg_event->setSecurityToken( SecurityManager::generateToken(source_notif->getSender_User()) );
	//BusManager::postMessage( msg_event, _Bus_Building );
	BusCore.postMessage(msg_event);
	return WRET_OK;
 }

















